"""
适配器异常类

本模块定义了市场数据服务适配器相关的所有异常类。
通过明确的异常类型，可以更好地处理各种错误情况。

异常层次结构：
```
AdapterError (基类)
├── AdapterConnectionError      # 连接相关错误
├── AdapterAuthenticationError  # 认证相关错误
├── AdapterDataError            # 数据相关错误
│   ├── AdapterDataFormatError  # 数据格式错误
│   └── AdapterDataValidationError  # 数据验证错误
├── AdapterTimeoutError         # 超时错误
├── AdapterRateLimitError       # 限流错误
├── AdapterNotFoundError        # 资源未找到
└── AdapterConfigError          # 配置错误
```

使用示例：
```python
from app.adapters.core.exceptions import (
    AdapterConnectionError,
    AdapterTimeoutError
)

try:
    await adapter.connect()
except AdapterConnectionError as e:
    logger.error(f"连接失败: {e}")
    # 重试逻辑
except AdapterTimeoutError as e:
    logger.error(f"连接超时: {e}")
    # 超时处理
```

作者: 架构组
创建日期: 2025-10-04
"""

from typing import Optional, Dict, Any


class AdapterError(Exception):
    """
    适配器基础异常类
    
    所有适配器相关异常的基类。
    捕获此异常可以捕获所有适配器相关的错误。
    
    Attributes:
        message: 错误消息
        adapter_name: 适配器名称
        details: 额外的错误详情
    
    示例：
        >>> raise AdapterError(
        ...     "数据源异常",
        ...     adapter_name="TushareAdapter",
        ...     details={"error_code": "40001"}
        ... )
    """
    
    def __init__(
        self,
        message: str,
        adapter_name: Optional[str] = None,
        details: Optional[Dict[str, Any]] = None
    ):
        self.message = message
        self.adapter_name = adapter_name
        self.details = details or {}
        
        # 构建完整错误消息
        full_message = message
        if adapter_name:
            full_message = f"[{adapter_name}] {message}"
        
        super().__init__(full_message)
    
    def __str__(self) -> str:
        """字符串表示"""
        base_str = self.message
        if self.adapter_name:
            base_str = f"[{self.adapter_name}] {base_str}"
        if self.details:
            base_str += f" | Details: {self.details}"
        return base_str
    
    def __repr__(self) -> str:
        """开发者友好的表示"""
        return (
            f"{self.__class__.__name__}("
            f"message='{self.message}', "
            f"adapter_name='{self.adapter_name}', "
            f"details={self.details})"
        )


# ==================== 连接相关异常 ====================

class AdapterConnectionError(AdapterError):
    """
    连接错误
    
    当适配器无法建立或维护与数据源的连接时抛出。
    
    常见场景：
    - 网络不可达
    - 服务器拒绝连接
    - 连接超时
    - 连接意外断开
    
    示例：
        >>> raise AdapterConnectionError(
        ...     "无法连接到Tushare API服务器",
        ...     adapter_name="TushareAdapter",
        ...     details={"host": "api.tushare.pro", "port": 443}
        ... )
    """
    pass


class AdapterAuthenticationError(AdapterError):
    """
    认证错误
    
    当身份验证失败时抛出。
    
    常见场景：
    - Token无效或过期
    - API Key错误
    - 用户名/密码错误
    - 权限不足
    
    示例：
        >>> raise AdapterAuthenticationError(
        ...     "API Token已过期",
        ...     adapter_name="TushareAdapter",
        ...     details={"token": "xxx***xxx"}
        ... )
    """
    pass


# ==================== 数据相关异常 ====================

class AdapterDataError(AdapterError):
    """
    数据错误
    
    当数据处理过程中发生错误时抛出。
    
    常见场景：
    - 数据格式不正确
    - 数据不完整
    - 数据验证失败
    - 无法解析响应
    
    示例：
        >>> raise AdapterDataError(
        ...     "无法解析API响应",
        ...     adapter_name="YahooFinanceAdapter",
        ...     details={"response": "<!DOCTYPE html>..."}
        ... )
    """
    pass


class AdapterDataFormatError(AdapterDataError):
    """
    数据格式错误
    
    当接收到的数据格式不符合预期时抛出。
    
    常见场景：
    - JSON解析失败
    - CSV格式错误
    - 字段缺失
    - 数据类型不匹配
    
    示例：
        >>> raise AdapterDataFormatError(
        ...     "预期JSON格式，但接收到HTML",
        ...     adapter_name="CTPAdapter",
        ...     details={"expected": "application/json", "actual": "text/html"}
        ... )
    """
    pass


class AdapterDataValidationError(AdapterDataError):
    """
    数据验证错误
    
    当数据内容不符合业务规则时抛出。
    
    常见场景：
    - 价格为负数
    - 时间戳无效
    - 高价低于低价
    - 数值超出合理范围
    
    示例：
        >>> raise AdapterDataValidationError(
        ...     "K线数据不合法：最高价低于最低价",
        ...     adapter_name="TushareAdapter",
        ...     details={"high": 10.0, "low": 10.5}
        ... )
    """
    pass


# ==================== 超时相关异常 ====================

class AdapterTimeoutError(AdapterError):
    """
    超时错误
    
    当操作超过预定时间限制时抛出。
    
    常见场景：
    - HTTP请求超时
    - WebSocket连接超时
    - 数据库查询超时
    - 响应等待超时
    
    Attributes:
        timeout: 超时时间（秒）
    
    示例：
        >>> raise AdapterTimeoutError(
        ...     "请求超时",
        ...     adapter_name="TushareAdapter",
        ...     details={"timeout": 30, "url": "http://api.tushare.pro"}
        ... )
    """
    
    def __init__(
        self,
        message: str,
        adapter_name: Optional[str] = None,
        timeout: Optional[float] = None,
        details: Optional[Dict[str, Any]] = None
    ):
        details = details or {}
        if timeout is not None:
            details['timeout'] = timeout
        super().__init__(message, adapter_name, details)
        self.timeout = timeout


# ==================== 限流相关异常 ====================

class AdapterRateLimitError(AdapterError):
    """
    限流错误
    
    当达到API速率限制时抛出。
    
    常见场景：
    - 超过每秒请求次数
    - 超过每日请求配额
    - 并发连接数过多
    
    Attributes:
        retry_after: 建议重试的等待时间（秒）
        limit: 速率限制值
        current: 当前使用量
    
    示例：
        >>> raise AdapterRateLimitError(
        ...     "超过API速率限制",
        ...     adapter_name="TushareAdapter",
        ...     details={
        ...         "limit": 100,
        ...         "current": 105,
        ...         "retry_after": 60
        ...     }
        ... )
    """
    
    def __init__(
        self,
        message: str,
        adapter_name: Optional[str] = None,
        retry_after: Optional[int] = None,
        limit: Optional[int] = None,
        current: Optional[int] = None,
        details: Optional[Dict[str, Any]] = None
    ):
        details = details or {}
        if retry_after is not None:
            details['retry_after'] = retry_after
        if limit is not None:
            details['limit'] = limit
        if current is not None:
            details['current'] = current
        
        super().__init__(message, adapter_name, details)
        self.retry_after = retry_after
        self.limit = limit
        self.current = current


# ==================== 资源相关异常 ====================

class AdapterNotFoundError(AdapterError):
    """
    资源未找到错误
    
    当请求的资源不存在时抛出。
    
    常见场景：
    - 标的代码不存在
    - 数据时间范围无数据
    - API端点不存在
    - 适配器未注册
    
    示例：
        >>> raise AdapterNotFoundError(
        ...     "标的代码不存在",
        ...     adapter_name="TushareAdapter",
        ...     details={"symbol": "999999.SZ"}
        ... )
    """
    pass


class AdapterNotSupportedError(AdapterError):
    """
    功能不支持错误
    
    当适配器不支持某个功能时抛出。
    
    常见场景：
    - 数据源不提供某类数据
    - 不支持某个时间周期
    - 不支持某个市场
    
    示例：
        >>> raise AdapterNotSupportedError(
        ...     "Tushare不支持实时订单簿数据",
        ...     adapter_name="TushareAdapter",
        ...     details={"feature": "订单簿订阅"}
        ... )
    """
    pass


# ==================== 配置相关异常 ====================

class AdapterConfigError(AdapterError):
    """
    配置错误
    
    当适配器配置不正确时抛出。
    
    常见场景：
    - 缺少必需的配置项
    - 配置值格式错误
    - 配置值不合法
    
    示例：
        >>> raise AdapterConfigError(
        ...     "缺少必需的配置项: token",
        ...     adapter_name="TushareAdapter",
        ...     details={"missing_fields": ["token"]}
        ... )
    """
    pass


# ==================== 订阅相关异常 ====================

class AdapterSubscriptionError(AdapterError):
    """
    订阅错误
    
    当实时数据订阅过程中发生错误时抛出。
    
    常见场景：
    - 订阅标的超过限制
    - WebSocket连接失败
    - 订阅被拒绝
    
    示例：
        >>> raise AdapterSubscriptionError(
        ...     "超过最大订阅标的数量",
        ...     adapter_name="CTPAdapter",
        ...     details={"max_symbols": 50, "current": 51}
        ... )
    """
    pass


# ==================== 辅助函数 ====================

def wrap_exception(
    exception: Exception,
    adapter_name: str,
    context: Optional[str] = None
) -> AdapterError:
    """
    包装原始异常为适配器异常
    
    用于将底层库的异常转换为统一的适配器异常。
    
    Args:
        exception: 原始异常
        adapter_name: 适配器名称
        context: 上下文信息
    
    Returns:
        AdapterError: 包装后的异常
    
    示例：
        >>> try:
        ...     response = requests.get(url)
        ... except requests.Timeout as e:
        ...     raise wrap_exception(e, "TushareAdapter", "查询K线数据")
    """
    message = str(exception)
    if context:
        message = f"{context}: {message}"
    
    # 根据异常类型选择合适的AdapterError子类
    if isinstance(exception, (ConnectionError, OSError)):
        return AdapterConnectionError(
            message,
            adapter_name=adapter_name,
            details={"original_exception": type(exception).__name__}
        )
    elif isinstance(exception, TimeoutError):
        return AdapterTimeoutError(
            message,
            adapter_name=adapter_name,
            details={"original_exception": type(exception).__name__}
        )
    elif isinstance(exception, (ValueError, TypeError)):
        return AdapterDataError(
            message,
            adapter_name=adapter_name,
            details={"original_exception": type(exception).__name__}
        )
    else:
        return AdapterError(
            message,
            adapter_name=adapter_name,
            details={"original_exception": type(exception).__name__}
        )


def is_retryable_error(error: Exception) -> bool:
    """
    判断错误是否可重试
    
    某些错误是暂时性的，可以通过重试解决。
    
    Args:
        error: 异常对象
    
    Returns:
        bool: True表示可重试，False表示不可重试
    
    示例：
        >>> try:
        ...     await adapter.query_bars(...)
        ... except AdapterError as e:
        ...     if is_retryable_error(e):
        ...         # 重试逻辑
        ...         pass
        ...     else:
        ...         # 放弃并记录错误
        ...         pass
    """
    # 可重试的错误类型
    retryable_types = (
        AdapterConnectionError,
        AdapterTimeoutError,
        AdapterRateLimitError,
    )
    
    return isinstance(error, retryable_types)


def get_retry_delay(error: Exception, attempt: int = 1) -> float:
    """
    获取建议的重试延迟时间（秒）
    
    根据错误类型和重试次数计算合适的等待时间。
    
    Args:
        error: 异常对象
        attempt: 重试次数（从1开始）
    
    Returns:
        float: 建议的延迟时间（秒）
    
    示例：
        >>> for attempt in range(1, 4):
        ...     try:
        ...         await adapter.connect()
        ...         break
        ...     except AdapterConnectionError as e:
        ...         delay = get_retry_delay(e, attempt)
        ...         await asyncio.sleep(delay)
    """
    # 如果是限流错误，使用指定的重试时间
    if isinstance(error, AdapterRateLimitError) and error.retry_after:
        return float(error.retry_after)
    
    # 否则使用指数退避策略
    # 基础延迟 * 2^(attempt-1)，最大60秒
    base_delay = 2.0
    max_delay = 60.0
    delay = min(base_delay * (2 ** (attempt - 1)), max_delay)
    
    return delay

